#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sim_i2c_dev.h"


#define ENDEBUG 1
#if ENDEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif
/*FangJinHao
模拟IIC
*/

#define SCL_PIN 4
#define SDA_PIN 5 

#define SDA_HIGH gpio_set_level(SDA_PIN, 1)
#define SDA_LOW  gpio_set_level(SDA_PIN, 0)

#define SCL_HIGH gpio_set_level(SCL_PIN, 1)
#define SCL_LOW  gpio_set_level(SCL_PIN, 0)

#define SDA_SET(level) gpio_set_level(SDA_PIN, level)

#define set_sda_input() gpio_set_direction(SDA_PIN,GPIO_MODE_INPUT) //开漏输入
#define set_sda_output() gpio_set_direction(SDA_PIN,GPIO_MODE_OUTPUT) //开漏输出
#define get_sda()  hal_gpio_get_level(SDA_PIN)

// static uint8_t iic_register = 0; 

void sim_i2c_gpio_disable(void)
{
    gpio_intput_init(SCL_PIN, 0, 0, GPIO_INTR_DISABLE);
    gpio_intput_init(SDA_PIN, 0, 0, GPIO_INTR_DISABLE);
    // iic_register = 0;
}

void sim_i2c_gpio_enable(void)
{
    gpio_outputOD_init(SCL_PIN);
    gpio_outputOD_init(SDA_PIN);
    hal_gpio_outSet(SCL_PIN, 1);
    hal_gpio_outSet(SDA_PIN, 1);
    // iic_register = 1;
}

// 延时函数，用于模拟I2C时序
static void i2c_delay(void)
{
    // esp_rom_delay_us(1);//300K
    // esp_rom_delay_us(2);//186K
    esp_rom_delay_us(3);//135K
}

static void i2c_start()
{
    set_sda_output();
    SDA_HIGH;
    i2c_delay();
    SCL_HIGH;
    i2c_delay();
    SDA_LOW;
    i2c_delay();
    SCL_LOW;
}

static void i2c_stop()
{
    set_sda_output();
    SDA_LOW;
    i2c_delay();
    SCL_HIGH;
    i2c_delay();
    SDA_HIGH;
    i2c_delay();
}

// 发送一个字节（包括从机地址和读写位）
// 返回0表示收到ACK，非0表示未收到ACK
static uint8_t i2c_send_byte(uint8_t byte)
{
    uint8_t bit,ack;
    set_sda_output();
    for (bit = 0; bit < 8; bit++)
    {
        SDA_SET((byte & 0x80) ? 1 : 0);
        byte <<= 1;
        i2c_delay();
        SCL_HIGH;
        i2c_delay();
        SCL_LOW;
    }
    // 读取ACK
    set_sda_input(); // 释放SDA线
    i2c_delay();
    SCL_HIGH;
    i2c_delay();
    ack = get_sda();
    SCL_LOW;
    i2c_delay();

    set_sda_output(); //这里可以设置成输出状态，但是要注意，设置高低电平不然，波形会有异常
    SDA_LOW;

    return ack;
}

// 读取一个字节并发送ACK（如果需要）
static uint8_t i2c_read_byte(uint8_t send_ack)
{
    uint8_t byte = 0;
    uint8_t bit;

    set_sda_input(); // 将SDA设置为输入以读取数据
    for (bit = 0; bit < 8; bit++)
    {
        SCL_HIGH;
        i2c_delay();
        byte = (byte << 1) | (get_sda() & 0x01);
        SCL_LOW;
        i2c_delay();
    }

    set_sda_output(); // 恢复SDA为输出以发送ACK
    // 发送ACK  
    SDA_SET(send_ack);  
    SCL_HIGH;  
    i2c_delay();  
    SCL_LOW;  
    i2c_delay();  
  
    // 恢复SDA为高电平（虽然在下一个时钟周期开始时SDA应该已经被释放为高阻态，这里设置也无妨）  
    // SDA_HIGH;
    return byte;
}

/*向寄存器发送一位数据*/
uint8_t i2c_write_reg_onebyte(uint8_t dev_addr, uint8_t reg_addr, uint8_t data)
{
    i2c_start();
    if (!i2c_send_byte(dev_addr))
    {                            // 发送从机地址+写位
        i2c_send_byte(reg_addr); // 发送寄存器地址
        i2c_send_byte(data);     // 发送数据
        i2c_stop();
        return 0;
    }
    else
    {
        //没找到设备
        i2c_stop();
        debug("iic error no_device dev_addr 0x%x reg 0x%x\n",dev_addr,reg_addr);
        return 0xff;
    }
}

uint8_t i2c_write_reg(uint8_t slaveAddr, uint8_t regAddr, uint8_t *data, uint16_t nbytes)
{
    i2c_start();
    if (!i2c_send_byte(slaveAddr))
    { 
        i2c_send_byte(regAddr);
        for (uint8_t i = 0; i < nbytes; i++)
        {
            i2c_send_byte(data[i]);
        }
        i2c_stop();
        return 0;
    }else
    {
        i2c_stop();
        debug("iic error no_device dev_addr 0x%x reg 0x%x\n",slaveAddr,regAddr);
        return 0xff;
    }
}

uint8_t i2c_read_reg(uint8_t slaveAddr, uint8_t regAddr, uint8_t *buffer, uint16_t nbytes)
{  
    i2c_start();  
    if (!i2c_send_byte(slaveAddr)) { // 发送从机地址+写位  
        i2c_send_byte(regAddr);
        i2c_start(); // 重复起始条件，改变读写位  
        i2c_send_byte(slaveAddr | 1);  // 发送从机地址+读位  
            for (uint8_t i = 0; i < nbytes - 1; i++)
            { // 读取len-1个字节，因为最后一个字节后不需要发送ACK  
                buffer[i] = i2c_read_byte(0); // 读取数据，发送ACK  
            }  
            buffer[nbytes - 1] = i2c_read_byte(0); // 读取最后一个字节，不发送ACK  
            i2c_delay();
            i2c_stop(); // 停止条件  
            return 0;  
    }else{
        i2c_stop();
        debug("iic error no_device dev_addr 0x%x reg 0x%x\n",slaveAddr,regAddr);
        return 0xff;}
}


